package com.daoyi.redis.aop;

import com.daoyi.redis.annotations.GlobalLock;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author daoyi
 * @date 2020-12-13
 */

@Aspect
public class GlobalLockAop {

    @Resource
    private RedissonClient redissonClient;

    private Logger logger = LoggerFactory.getLogger(this.getClass());


    @Around("@annotation(com.daoyi.redis.annotations.GlobalLock)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //切点所在的类
        Class targetClass = joinPoint.getTarget().getClass();
        //使用了注解的方法
        String methodName = joinPoint.getSignature().getName();
        //参数类型
        Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        Method method = targetClass.getMethod(methodName, parameterTypes);
        Object[] arguments = joinPoint.getArgs();
        final String lockName = getLockKey(method, arguments);
        return doLock(joinPoint, lockName);
    }

    /**
     * 分布式锁调用
     */
    private Object doLock(ProceedingJoinPoint pjp, String lockKey) throws Throwable {
        logger.info("开始调用分布式锁:{}",lockKey);
        Object result = null;
        RLock lock = null;
        boolean isLock = false;
        Exception exception = null;
        try {
            lock = redissonClient.getLock(lockKey);
            lock.lock();
            isLock = true;
            result = pjp.proceed();

        } catch (Exception e) {
            exception = e;
        } finally {
            if (isLock) {
                try {
                    lock.unlock();
                } catch (Exception e) {
                    logger.error("分布式锁释放异常：", e);
                }
            }
            if (exception != null) {
                throw exception;
            }
        }
        return result;
    }

    /**
     * 获取分布锁key
     */
    private String getLockKey(Method method, Object[] arguments) {
        Objects.requireNonNull(method);
        String lockKey = null;
        GlobalLock globalLock = method.getAnnotation(GlobalLock.class);
        if (globalLock.argsIndex() >= 0) {
            lockKey = globalLock.lockKey() + arguments[globalLock.argsIndex()].toString();
        } else {
            lockKey = globalLock.lockKey();
        }
        if (StringUtils.isEmpty(lockKey)) {
            throw new RuntimeException("分布锁注解配置错误！lockkey或锁参数索引错误");
        }
        return lockKey;
    }


}
